package com.kasax.bus.corel;

import android.os.Handler;
import android.os.Looper;

import com.kasax.bus.anno.Subscribe;
import com.kasax.bus.bean.SubscribeMethod;
import com.kasax.bus.utils.LogUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EventBus {
    private static volatile EventBus sInstance = new EventBus();
    private final Handler handler;
    private Map<Object, List<SubscribeMethod>> cacheMap;
    private ExecutorService threadPool;

    private EventBus() {
        cacheMap = new HashMap<Object, List<SubscribeMethod>>();
        handler = new Handler(Looper.getMainLooper());
        // 缓存的线程池
        threadPool = Executors.newCachedThreadPool();
    }

    public static EventBus getDefault() {
        return sInstance;
    }

    /**
     * 注册
     */
    public void register(Object subscriber) {
        Class<?> aClass = subscriber.getClass();
        List<SubscribeMethod> subscribeMethods = cacheMap.get(subscriber);
        // 不重复注册
        if (subscribeMethods == null) {
            subscribeMethods = getSubscribeMethods(subscriber);
            cacheMap.put(subscriber, subscribeMethods);
        }

        showMapData();
    }

    private void showMapData() {
        for (Map.Entry<Object, List<SubscribeMethod>> entry : cacheMap.entrySet()) {
            LogUtils.i(getClass(), "showMapData key: " + entry.getKey() + " , data: " + entry.getValue());
        }
    }

    /**
     * 遍历能够接收事件的方法
     *
     * @param subscriber
     * @return
     */
    private List<SubscribeMethod> getSubscribeMethods(Object subscriber) {
        ArrayList<SubscribeMethod> list = new ArrayList<>();
        Class<?> aClass = subscriber.getClass();
        // 查找过程 subscriber --- 》BaseActivity    ------->Activitiy
        while (aClass != null) {
            String name = aClass.getName();
            // 判断包名，忽略系统的
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.") || name.startsWith("androidx.")) {
                break;
            }

            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                Subscribe annotation = method.getAnnotation(Subscribe.class);
                if (annotation == null) {
                    continue;
                }
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length != 1) {
                    throw new RuntimeException("eventbusz只能接收一个参数");
                }

                ThreadMode threadMode = annotation.threadMode();
                SubscribeMethod subscribeMethod = new SubscribeMethod(method, threadMode, parameterTypes[0]);
                list.add(subscribeMethod);
            }

            // 从当前的类一直往父类进行查找
            aClass = aClass.getSuperclass();
        }
        return list;
    }

    // 取消注册
    public void unRegister(Object subscriber) {
        Class<?> aClass = subscriber.getClass();
        List<SubscribeMethod> list = cacheMap.get(aClass);
        if (list != null) {
            cacheMap.remove(subscriber);
        }
    }

    /**
     * 发送消息
     *
     * @param obj
     */
    public void post(final Object obj) {
        // 找到方法，然后进行调用
        Set<Object> set = cacheMap.keySet();
        Iterator<Object> iterator = set.iterator();
        while (iterator.hasNext()) {
            // 拿到注册的类
            Object next = iterator.next();
            // 获取类中所有添加注解的方法
            List<SubscribeMethod> list = cacheMap.get(next);
            for (SubscribeMethod subscribeMethod : list) {
                // 判断这个方法是否应该接收这个事件
                if (subscribeMethod.getEventType().isAssignableFrom(obj.getClass())) {
                    switch (subscribeMethod.getThreadMode()) {
                        case MAIN:
                            // 如果接收的方法是在主线程执行的情况
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                invoke(subscribeMethod, next, obj);
                            } else {
                                //post方法执行在子线程中，接收消息在主线程中
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(subscribeMethod, next, obj);
                                    }
                                });
                            }
                            break;
                        case ASYNC:
                            //接收方法在子线程种情况
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                threadPool.execute(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(subscribeMethod, next, obj);
                                    }
                                });
                            } else {
                                invoke(subscribeMethod, next, obj);
                            }
                            break;
                        case POSTING:
                            break;
                    }
                }
            }
        }
    }

    private void invoke(SubscribeMethod subscribeMethod, Object next, Object obj) {
        try {
            Method method = subscribeMethod.getMethod();
            LogUtils.i(getClass(),
                    "invoke subscribeMethod: " + subscribeMethod + " , next: " + next + " , obj: " + obj + " , method-name: " + method.getName());
            method.invoke(next, obj);
        } catch (Exception e) {
            LogUtils.e(getClass(), e);
        }
    }
}
